#pragma once

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <time.h>
#include <stdarg.h>
#include <fstream>
#include <pthread.h>
#include "LockGuard.hpp"
#include "Comm.hpp"
namespace log_ns
{

#define SCREEN 1
#define FILE_TYPE 2

    pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER;
    const std::string glogfile = "./log.txt";



    class LogMessage
    {
    public:
        std::string _level;
        pid_t _pid;
        std::string _filename;
        int _filenumber;
        std::string _current_time;
        std::string _message_info;
    };

    std::string LevelToString(int level)
    {
        switch (level)
        {
        case DEBUG:
            return "DEBUG";
        case INFO:
            return "INFO";
        case WARNING:
            return "WARNING";
        case ERROR:
            return "ERROR";
        case FATAL:
            return "FATAL";
        default:
            return "UNKNOW";
        }
    }

    std::string GetTime()
    {
        time_t t = time(nullptr);

        struct tm *cur_time = localtime(&t);

        char buff[128];

        snprintf(buff, sizeof(buff), "%d-%d-%d %02d:%02d:%02d",
                 cur_time->tm_year + 1900,
                 cur_time->tm_mon + 1,
                 cur_time->tm_mday,
                 cur_time->tm_hour,
                 cur_time->tm_min,
                 cur_time->tm_sec);
        return buff;
    }

    // log.logMessage(__FILENAME__ , __LINE__,"this is %d message",x);
    class Log
    {
    public:
        Log(const std::string &filename = glogfile)
            : _type(SCREEN), _log_file(filename)
        {
        }
        void FlushLogScreen(const LogMessage &lg)
        {
            printf("[%s][%d][%s][%d][%s] %s", lg._level.c_str(),
                   lg._pid,
                   lg._filename.c_str(),
                   lg._filenumber,
                   lg._current_time.c_str(),
                   lg._message_info.c_str());
        }

        void FlushLogFile(const LogMessage &lg)
        {
            std::ofstream out(_log_file, std::ostream::app);
            if (!out.is_open())
                return;
            char buff[1024];
            snprintf(buff, sizeof(buff), "[%s][%d][%s][%d][%s] %s", lg._level.c_str(),
                     lg._pid,
                     lg._filename.c_str(),
                     lg._filenumber,
                     lg._current_time.c_str(),
                     lg._message_info.c_str());
            out.write(buff, strlen(buff));
            out.close();
        }

        void Flush(const LogMessage &lg)
        {
            LockGuard lock(&gmutex);
            switch (_type)
            {
            case SCREEN:
                FlushLogScreen(lg);
                break;
            case FILE_TYPE:
                FlushLogFile(lg);
                break;
            }
        }

        void Enable(int type)
        {
            _type = type;
        }

        void logMessage(std::string filename, int filenumber, int level, const char *info, ...)
        {
            LogMessage lg;

            lg._level = LevelToString(level);
            lg._pid = getpid();
            lg._filename = filename;
            lg._filenumber = filenumber;
            lg._current_time = GetTime();

            va_list ap;
            va_start(ap, info);
            char log_info[1024];
            vsnprintf(log_info, sizeof(log_info), info, ap);
            va_end(ap);

            lg._message_info = log_info;

            // 打印出来
            Flush(lg);
        }

        ~Log()
        {
        }

    private:
        int _type;
        std::string _log_file;
    };

    Log lg;

#define LOG(Level, Format, ...)                                          \
    do                                                                   \
    {                                                                    \
        lg.logMessage(__FILE__, __LINE__, Level, Format, ##__VA_ARGS__); \
    } while (0)
#define EnableScreen()     \
    do                     \
    {                      \
        lg.Enable(SCREEN); \
    } while (0)
#define EnableFILE()          \
    do                        \
    {                         \
        lg.Enable(FILE_TYPE); \
    } while (0)
}